package com.itheima.reggie.controller;

import com.itheima.reggie.common.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.UUID;

/**
 * @author 程达
 * @PackageName:com.itheima.reggie.controller
 * @ClassName:CommonController
 * @Description:处理公共业务的的处理器 上传下载
 * 不属于某个模块 整个项目所有模块上传下载 我都处理
 * @date 2022/5/26 8:48
 **/
@RestController
@Slf4j
@RequestMapping("/common")
@Api(tags = "文件上传下载通用相关接口")
public class CommonController {
    // 1.去配置文件中 找到 路径变量 赋值
    @Value("${reggie.path}")
    private String basePath;

    /**
     * 实现文件上传的处理
     * 1:上传的文件 是不是应该存在服务器的一个固定位置,
     * 下载还得找这个位置.
     * 先把上传的位置,定义在全局变量上(yml中)
     * 文件上传，也称为upload，
     * <p>
     * 上传逻辑:
     * <p>
     * 1). 获取文件的原始文件名, 通过原始文件名获取文件后缀
     * <p>
     * 2). 通过UUID重新声明文件名, 文件名称重复造成文件覆盖
     * <p>
     * 3). 创建文件存放目录
     * <p>
     * 4). 将上传的临时文件转存到指定位置
     */

    @PostMapping("/upload")
    @ApiOperation(value = "文件上传接口")
    //   pring框架在spring-web包中对文件上传进行了封装，大大简化了服务端代码，
    //   通过MultipartFile类型的参数即可接收上传的文件, 方法形参的名称需要与页面的file域的name属性一致。
    //   我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件，如下：
    public R<String> upload(MultipartFile file) {//MultipartFile类型的参数即可接收上传的文件，
        // 当这个参数有值的时候，那个图片已经拿到了在tomcat临时目录下
        log.info("我们看一下这是个嘛..." + file);
        //2.获取上传文件的原始名字 getOriginalFilename这个MultipartFile里面的方法就可以
        String originalFilename = file.getOriginalFilename();
        log.info("file就是上传的文件信息,file的名字叫啥呢" + originalFilename);
        //  原始名字  originalFilename    songxiaobao.jpg
        // 大家能不能根据以前学习的 把 后缀名 .jpg截取出来
        //        originalFilename.lastIndexOf(".")// .最后一次出现的索引
        //  是不是从.的最后的索引  进行截取
        //String substring(int start) 从指定位置开始，到末尾结束，截取该字符串，返回新字符串
        String fileHouzhui = originalFilename.substring(originalFilename.lastIndexOf("."));//拿到.jpg
        // suffix 后缀名

        // 我们要把图片存到服务上 图片的名字是随机的 但是图片的类型 必须和原始文件一致
        //        3.1UUID.randomUUID().toString()  新的功能  生成随机的字符串
        // 3.2生成的随机字符串要作为什么出现呢?
        String fileName = UUID.randomUUID().toString() + fileHouzhui;//   拼接成：名字.后缀

        // 是不是想把   MultipartFile file  上传文件里面的数据 转换到 我们自己的文件里面
        // 自己的文件在哪放着呢?  yml配置了
        // 这个 basePath 有可能不存在

        //4.创建文件存放目录
        File dir = new File(basePath);//把文件的基本目录进行封装 判断它是否存在
        if (!dir.exists()) {//不存在
            //创建出来
            dir.mkdirs();
        }
        //我们能不能指定服务器 当前要存储文件的路径了
        //5.将上传的临时文件转存到指定位置
        //File(File parent,String chile)  父路径的对象形式+子路径 确定文件位置
        File uploadFile = new File(dir, fileName);//我们接收上传文件的  新文件构建出来
        //  这个新文件的数据 来自 上传的数据   MultipartFile file
        //  按照以前方式 是不是边读编写......
        //  现在高级了  直接通过   MultipartFile file
        try {
            //6.现在只是有了这个文件位置，和文件名字，但是还没有内容，要通过下面这个方法把图片给拷贝过去MultipartFile提供的
            //把file的内容移动(拷贝)到这个文件里面去
            file.transferTo(uploadFile);//把 上传的数据 转换到 新文件中
            // 底层是  copy(输入流,输出流)
            // 如果没异常 搞定了
            //7.把新名字返回给前端因为下载的时需要这个文件名字当参数
            return R.success(fileName);//因为前端要你的  文件在服务器的名字
        } catch (IOException e) {
            e.printStackTrace(); // 你的新文件如果不存在呢?  有权限问题呢,都会在这里爆出...
        }
        return R.error("程序上传过程中遇到问题了");

    }

    /**
     * 后端要出预览业务    就是 下载
     * 需要参数
     * 前端传过来的文件名
     * 响应对象
     * 返回值写什么?  void
     * 我们以前写返回值,是不是把数据转换成json写响应体中.....
     * 现在 是不是我们自己手动用响应对象 写字节数据了 ,那还需不需要返回值了?
     * <p>
     * 怎么处理下载呢
     * 是不是要根据用户需要的 文件名名字 ,,,,找到这个文件
     * 把文件数据读到程序中.
     * 通过  响应对象的字节输出流  写到浏览器
     * 文件下载 download
     * <p>
     * 在文件上传成功后，在 handleAvatarSuccess
     * 方法中获取文件上传成功之后返回的数据(文件名)，
     * 然后调用 /common/download?name=xxx 进行文件的下载。
     * 7.在这里，我们想让上传的照片能够在页面展示出来，
     * 所以我们就需要在服务端将文件以流的形式写回浏览器。
     * 这里用get的方式是/download?name=${response.data}
     * <p>
     * <p>
     * 1). 定义输入流，通过输入流读取文件内容
     * <p>
     * 2). 通过response对象，获取到输出流
     * <p>
     * 3). 通过response对象设置响应数据格式(image/jpeg)
     * <p>
     * 4). 通过输入流读取文件数据，然后通过上述的输出流写回浏览器
     * <p>
     * 5). 关闭资源
     */
    @GetMapping("/download")
    @ApiOperation(value = "文件预览下载相关接口")
    public void download(String name, HttpServletResponse response) {
        log.info("[进行文件下载,下载的文件名 name:{}]", name);
        //先读后写
        try {
            //1.1 要下载的文件--要读的文件在哪?  basePath 下的 name文件
            File file = new File(basePath, name);
            // 这个要下载的文件是不是需要 字节输入流去读? 需要 先读取到这个文件
            //1.2 定义输入流，通过输入流读取文件内容
            FileInputStream in = new FileInputStream(file);
            //读到的数据写哪里 写到 response字节输出流
            //2. 通过response对象，获取到输出流
            ServletOutputStream out = response.getOutputStream();

            //开始 边读编写  注意  你写的内容是不是一个图片 这个是时候
            //在响应头上告知 浏览器 要以图片形式打开

            //3.通过response对象设置响应数据格式(image/jpeg)
            response.setContentType("image/jpeg");//告知浏览器打开形式  设置响应数据类型

            //边读编写  --  忘了先复制  晚上十点在问在学
            int len = 0;
            byte[] buffer = new byte[8 * 1024];
            //4.1 通过输入流读取文件数据，然后通过上述的输出流写回浏览器
            while (((len = in.read(buffer)) != -1)) {
                //4.2 写出去
                out.write(buffer, 0, len);//读多少写多少
                out.flush();//刷一下
            }//代码走到这里，就已经完成了把文件读到并写到响应数据流里面了
            //释放资源
            out.close();//写不写都行 因为  response对象会自动管理
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}